package com.hcc.gateway.netty.context;

import cn.hutool.core.collection.CollectionUtil;
import com.hcc.gateway.netty.support.ByteBufUtils;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.handler.codec.http.multipart.MemoryAttribute;
import lombok.Data;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 请求报文
 *
 * @author hushengjun
 * @date 2021/12/26
 */
@Data
public class ReqContext {

    private String originalUri;
    private String uri;
    private HttpHeaders headers;
    private HttpMethod method;
    private String contentType;
    private ByteBuf content;
    private Map<String, Object> queryParams;
    private Map<String, Object> formData;
    private String stringBody;
    private String clientIp;

    public ReqContext(FullHttpRequest request) {
        this.headers = request.headers();
        this.method = request.method();
        this.contentType = request.headers().get("Content-Type") == null ? null : request.headers().get("Content-Type").trim().toLowerCase();
        this.content = request.content();
        this.originalUri = request.uri();
        if (request.uri().contains("?")) {
            this.uri = request.uri().substring(0, request.uri().indexOf("?"));
        } else {
            this.uri = request.uri();
        }
        this.queryParams = this.parseQueryParam(request.uri());
        this.formData = this.parseFormData(request);
        this.stringBody = this.parseStringContent(request);
    }

    /**
     * 解析查询参数
     * @param uri
     * @return
     */
    private Map<String, Object> parseQueryParam(String uri) {
        Map<String, Object> resultMap = new HashMap<>();
        QueryStringDecoder queryStringDecoder = new QueryStringDecoder(uri);
        queryStringDecoder.parameters().forEach((k, v) -> {
            Object value = null;
            if (CollectionUtil.isNotEmpty(v)) {
                if (v.size() == 1) {
                    value = v.get(0);
                } else {
                    value = v;
                }
            }
            resultMap.put(k, value);
        });

        return resultMap;
    }

    /**
     * 解析内容
     * @param request
     * @return
     */
    private String parseStringContent(FullHttpRequest request) {
        return ByteBufUtils.readStringFromByteBuf(request.content());
    }

    /**
     * 解析表单数据
     * @param request
     * @return
     */
    private Map<String, Object> parseFormData(FullHttpRequest request) {
        if (HttpMethod.POST.equals(request.method())
                || HttpMethod.PUT.equals(request.method())
                || HttpMethod.PATCH.equals(request.method())) {
            if (contentType.contains("x-www-form-urlencoded")) {
                Map<String, Object> paramMap = new HashMap<>();
                HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(new DefaultHttpDataFactory(false), request);
                List<InterfaceHttpData> interfaceHttpDatas = decoder.getBodyHttpDatas();
                for (InterfaceHttpData interfaceHttpData : interfaceHttpDatas) {
                    if (interfaceHttpData.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                        MemoryAttribute data = (MemoryAttribute) interfaceHttpData;
                        if (paramMap.get(data.getName()) != null) {
                            List<Object> values = new ArrayList<>();
                            values.add(paramMap.get(data.getName()));
                            values.add(data.getValue());
                            paramMap.put(data.getName(), values);
                        } else {
                            paramMap.put(data.getName(), data.getValue());
                        }
                    }
                }

                return paramMap;
            }
        }

        return Collections.emptyMap();
    }

}
